/**
 * Copyright (c) 2016-2019 人人开源 All rights reserved.
 * <p>
 * https://www.renren.io
 * <p>
 * 版权所有，侵权必究！
 */

package io.renren.interceptor;


import io.renren.annotation.ApiSignAuth;
import io.renren.annotation.Login;
import io.renren.common.exception.RRException;
import io.renren.config.ApiConfig;
import io.renren.util.JwtUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 权限(Token)验证
 *
 * @author Mark sunlightcs@gmail.com
 */
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    private Logger logger = LoggerFactory.getLogger(getClass());


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod;
        if (handler instanceof HandlerMethod) {
            handlerMethod = (HandlerMethod) handler;
        } else {
            return true;
        }
        // 获取方法上的注解
        ApiSignAuth signAuth = handlerMethod.getMethodAnnotation(ApiSignAuth.class);
        if (signAuth == null) {
            // 如果方法上的注解为空 则获取类的注解
            signAuth = handlerMethod.getMethod().getDeclaringClass().getAnnotation(ApiSignAuth.class);
        }
        //验证签名
        if (signAuth != null) {
            if (signAuth.isAuth()) {
                if (!verifySign(request)) {
                    throw new RRException("签名验证失败：API非法请求");
                }
            }
        }
        //验证token
        Login login = handlerMethod.getMethodAnnotation(Login.class);
        if (login == null) {
            login = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Login.class);
        }
        if (login != null) {
            verifyLogin(request, response);
        }

        return true;
    }

    /**
     * Description: 验证参数签名
     *
     * @author afeng
     * @date 2020/1/19 22:39
     */
    private boolean verifySign(HttpServletRequest request) throws Exception {
        String param = "";
        if (StringUtils.isNotBlank(request.getQueryString())) {
            param = URLDecoder.decode(request.getQueryString(), "utf-8");
        }
        if (StringUtils.isBlank(param)) {
            return true;
        }
        SortedMap<String, String> result = new TreeMap<>();
        String[] params = param.split("&");
        for (String s : params) {
            int index = s.indexOf("=");
            result.put(s.substring(0, index), s.substring(index + 1));
        }
        if (StringUtils.isEmpty(result.get("sign"))) {
            logger.debug("签名验证失败：缺少sign参数");
            return false;
        }
        StringBuilder sb = new StringBuilder();
        for (Map.Entry entry : result.entrySet()) {
            if (!"sign".equals(entry.getKey())) {
                //拼装参数,排除sign
                if (entry.getKey() != null && entry.getValue() != null) {
                    sb.append(entry.getKey()).append(entry.getValue());
                }
            }
        }
        logger.debug("签名前参数 : {}", sb.toString());
        String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
        logger.debug("签名 : {}", sign);
        return !StringUtils.isEmpty(sign) && result.get("sign").equals(sign);
    }

    /**
     * 登录验证
     *
     * @author afeng
     */
    private void verifyLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //从header中获取token
        String token = request.getHeader("Authorization");
        //如果header中不存在token，则从参数中获取token
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("token");
        }
        //token为空
        if (StringUtils.isBlank(token)) {
            throw new RRException("token不能为空");
        }
        //查询token信息
        String userId = JwtUtil.queryByToken(ApiConfig.TOKEN_KEY, token);
        if (StringUtils.isBlank(userId)) {
            throw new RRException("token解密失败");
        }
        if (!JwtUtil.verifyToken(token, userId, ApiConfig.TOKEN_KEY, ApiConfig.TOKEN_SECRET)) {
            throw new RRException("token失效，请重新登录");
        }
        //刷新token
        String refreshToken = JwtUtil.createToken(userId, ApiConfig.TOKEN_KEY,
                ApiConfig.TOKEN_SECRET, ApiConfig.TOKEN_EXPIRE_TIME);
        //设置token到响应头由前端获取
        response.setHeader("Authorization", refreshToken);
        //设置userId到request里，后续根据userId，获取用户信息
        request.setAttribute(ApiConfig.TOKEN_KEY, userId);
    }
}
